package middlewares

import (
	"boy-go/modules/system/model"
	"boy-go/modules/system/repository"
	"boy-go/pkg/iputils"
	"boy-go/pkg/security"
	"bytes"
	"fmt"
	"github.com/gin-gonic/gin"
	"io"
	"log"
	"reflect"
	"runtime"
	"time"
)

type BusinessType string

const (
	OTHER   BusinessType = "Other"   // 其他
	INSERT  BusinessType = "Insert"  // 新增
	UPDATE  BusinessType = "Update"  // 修改
	DELETE  BusinessType = "Delete"  // 删除
	GRANT   BusinessType = "Grant"   // 授权
	EXPORT  BusinessType = "Export"  // 导出
	IMPORT  BusinessType = "Import"  // 导入
	FORCE   BusinessType = "Force"   // 强退
	GENCODE BusinessType = "GenCode" // 生成代码
	CLEAN   BusinessType = "Clean"   // 清空数据
)

var businessTypeMap = map[BusinessType]int{
	OTHER:   0,
	INSERT:  1,
	UPDATE:  2,
	DELETE:  3,
	GRANT:   4,
	EXPORT:  5,
	IMPORT:  6,
	FORCE:   7,
	GENCODE: 8,
	CLEAN:   9,
}

type responseBodyWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (r responseBodyWriter) Write(data []byte) (int, error) {
	r.body.Write(data)
	return r.ResponseWriter.Write(data)
}

func getFunctionName(i interface{}) string {
	return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

// Logger 操作日志记录
func Logger(title string, businessType BusinessType) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 开始时间
		start := time.Now()
		var requestBody []byte
		if c.Request.Body != nil {
			requestBody, _ = io.ReadAll(c.Request.Body)
			// 重新设置请求体以便后续读取
			c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
		}
		// 替换默认的 ResponseWriter
		bodyWriter := &responseBodyWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
		c.Writer = bodyWriter
		defer func() {
			statue := 0
			errorMsg := ""
			if err := recover(); err != nil {
				statue = 1
				//errorMsg = fmt.Sprintf("panic: %v \n trace:\\n%s", err, string(debug.Stack()))
				errorMsg = fmt.Sprintf("panic: %v", err)
				//恢复panic
				panic(err)
			}
			handler := c.Handler()
			handlerName := getFunctionName(handler)
			fmt.Printf("Current handler: %s\n", handlerName)
			// 结束时间
			end := time.Now()
			// 求出花费的时间
			latency := end.Sub(start)
			useTime := int64(latency.Milliseconds())
			param := ""
			if requestBody != nil {
				param = string(requestBody)
			} else {
				param = c.Request.URL.Query().Encode()
			}
			path := c.Request.URL.Path
			clientIP := iputils.GetIp(c)
			method := c.Request.Method

			responseBody := bodyWriter.body.String()
			if businessType == EXPORT {
				responseBody = ""
			}
			btype := 0
			bv, have := businessTypeMap[businessType]
			if have {
				btype = bv
			}
			loginUser := security.GetLoginUser()
			operName := ""
			deptName := ""
			operLocation := ""
			if loginUser != nil {
				operName = loginUser.UserName
				deptName = *loginUser.User.Dept.DeptName
				operLocation = loginUser.LoginLocation
			}
			operatorType := 1 // 后台用户
			oper := &model.SysOperLog{
				Title:         &title,        // 模块标题
				BusinessType:  &btype,        // 业务类型（0其它 1新增 2修改 3删除）
				Method:        &handlerName,  // 方法名称
				RequestMethod: &method,       // 请求方式
				OperatorType:  &operatorType, // 操作类别（0其它 1后台用户 2手机端用户）
				OperName:      &operName,     // 操作人员
				DeptName:      &deptName,     // 部门名称
				OperUrl:       &path,         // 请求URL
				OperIp:        &clientIP,     // 主机地址
				OperLocation:  &operLocation, // 操作地点
				OperParam:     &param,        // 请求参数
				JsonResult:    &responseBody, // 返回参数
				Status:        &statue,       // 操作状态（0正常 1异常）
				ErrorMsg:      &errorMsg,     // 错误消息
				OperAt:        &start,        // 操作时间
				CostTime:      &useTime,      // 消耗时间
			}
			//保存操作日志 [异步]
			go _saveOpener(oper)
		}()
		// 处理请求
		c.Next()
	}
}

func _saveOpener(oper *model.SysOperLog) {
	addr := iputils.GetRealAddressByIP(*oper.OperIp)
	oper.OperLocation = &addr
	err := repository.NewSysOperLogRepository().Save(oper)
	if err != nil {
		log.Printf("保存操作日志失败：%v", err)
	}
}
